﻿using System;
using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Casamiel.Application;
using Casamiel.Common;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using NLog;

namespace Casamiel.API.Infrastructure.Filters
{
    /// <summary>
    /// Check login attribute.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Delegate)]
    public class CheckLoginAttribute : Attribute, IAsyncActionFilter
    {
        private readonly IMemberService _member;
        private readonly ICacheService _cacheService;
        private readonly IMemberCardService _memberCard;
        private readonly CasamielSettings _settings;
        private readonly NLog.ILogger logger = LogManager.GetLogger("OpenInfo");

       
        /// <summary>
        /// 
        /// </summary>
        /// <param name="memberRepository"></param>
        /// <param name="cacheService"></param>
        /// <param name="memberCard"></param>
        /// <param name="settings"></param>
        public CheckLoginAttribute(IMemberService memberRepository,
            ICacheService cacheService
            , IMemberCardService memberCard
            , IOptionsSnapshot<CasamielSettings> settings
           )
        {
            if (settings == null)
            {
                throw new ArgumentNullException(nameof(settings));
            }

            this._member = memberRepository;
            this._cacheService = cacheService;
            this._memberCard = memberCard;
            this._settings = settings.Value;
        }
        /// <summary>
        /// Ons the action execution async.
        /// </summary>
        /// <returns>The action execution async.</returns>
        /// <param name="context">Context.</param>
        /// <param name="next">Next.</param>
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (next == null)
            {
                throw new ArgumentNullException(nameof(next));
            }

            var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("cssupersecret_secretke!miel"));
            var tokenValidationParameters = new TokenValidationParameters
            {
                // The signing key must match!
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = signingKey,

                // Validate the JWT Issuer (iss) claim
                ValidateIssuer = true,
                ValidIssuer = "CasamielIssuer",

                // Validate the JWT Audience (aud) claim
                ValidateAudience = true,
                ValidAudience = "casamiel",

                // Validate the token expiry
                ValidateLifetime = true,

                // If you want to allow a certain amount of clock drift, set that here:
                ClockSkew = TimeSpan.Zero
            };

            var token = context.HttpContext.Request.Headers["u-token"];

            var userAgent = string.IsNullOrEmpty(context.HttpContext.Request.Headers["User-Agent"].ToString()) ? context.HttpContext.Request.Headers["UserAgent"].ToString() : context.HttpContext.Request.Headers["User-Agent"].ToString();
            int loginType = 2;
            if (userAgent.ToUpper(CultureInfo.InvariantCulture).Contains("CASAMIEL", StringComparison.CurrentCulture))
            {
                loginType = 1;
            }
            if (userAgent.ToUpper(CultureInfo.InvariantCulture).Contains("DONCO", StringComparison.CurrentCulture))
            {
                loginType = 1;
            }
            if (userAgent.Contains("MicroMessenger", StringComparison.CurrentCulture))
            {
                loginType = 3;//微信端
            }
            if (string.IsNullOrEmpty(token))
            {
                logger.Error($"无效token，User-Agent{userAgent},IP:{context.HttpContext.Request.GetUserIp()}");
                context.Result = new JsonResult(new { code = -1, msg = "token无效" });
                return;
            }

            var handler = new JwtSecurityTokenHandler();
            ClaimsPrincipal principal = null;
            SecurityToken validToken = null;
            try
            {
                principal = handler.ValidateToken(token, tokenValidationParameters, out validToken);
            }
            catch (SecurityTokenExpiredException ex)
            {
                Console.WriteLine(ex.Message);
                logger.Error($"token无效，User-Agent{userAgent},IP:{context.HttpContext.Request.GetUserIp()},{token}");
                context.Result = new JsonResult(new { code = -1, msg = "token无效" });
                return;
            }
            var validJwt = validToken as JwtSecurityToken;

            if (validJwt == null)
            {
                logger.Error($"token无效，User-Agent{userAgent},IP:{context.HttpContext.Request.GetUserIp()},{token}");

                context.Result = new JsonResult(new { code = -1, msg = "token无效" });
                return;
            }
            JwtSecurityToken dtoken = handler.ReadJwtToken(token);
            var rsa = new RSAHelper(RSAType.RSA2, Encoding.UTF8, RSAHelper.privateKey, RSAHelper.publicKey);

            var exp = dtoken.Payload.Exp;
            string mobile = rsa.Decrypt(dtoken.Payload.Jti);
            //  Console.WriteLine(new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds());
            if (exp < new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds())
            {
                logger.Error($"登录凭证已过期，请重新登陆，User-Agent{userAgent},IP:{context.HttpContext.Request.GetUserIp()},{token}");

                context.Result = new JsonResult(new { code = -1, msg = "登录凭证已过期，请重新登陆" });
                return;
            }
            else
            {
                //string key = Constant.TOKEN_PREFIX + mobile + "_" + loginType;
                var mtoken = await _member.FindAsync<ICasaMielSession>(mobile, loginType).ConfigureAwait(false);

                if (mtoken == null)
                {
                    logger.Error($"登录凭证无效，User-Agent{userAgent},IP:{context.HttpContext.Request.GetUserIp()},{token}");
                    context.Result = new JsonResult(new { code = -1, msg = "登录凭证无效" });
                    return;
                }

                if (mtoken.Access_Token != token)
                {

                    logger.Error($"登录凭证无效，User-Agent{userAgent},IP:{context.HttpContext.Request.GetUserIp()},{token}");

                    context.Result = new JsonResult(new { code = -1, msg = "登录凭证已过期，请重新登陆" });
                    return;
                }

            }
            await next().ConfigureAwait(false);
        }
    }
}
